﻿using System;
using System.Linq;
using Microsoft.Crm.Workflow;
using System.Workflow.ComponentModel.Compiler;
using Microsoft.Crm.Sdk;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using CrmContrib.Workflow.Activities.Opportunities;
using System.Xml.Linq;
using System.Web.Services.Protocols;
using System.Diagnostics;
    
namespace CrmContrib.Workflow.Activities.SalesOrders
{
    [CrmWorkflowActivity("Close sale orders", "CrmContrib \\ Sales Orders")]
    public class CloseSalesOrdersActivity : Activity
    {
        private const string invoiceCountFetchXml = "<fetch mapping='logical' aggregate='true' version='1.0'><entity name='invoice'><attribute name='invoiceid' aggregate='count' alias='count'/><filter><condition attribute='salesorderid' operator='eq' value='{0}'/></filter></entity></fetch>";

        [CrmInput("On or Before this date")]
        [ValidationOption(ValidationOption.Required)]
        public CrmDateTime Age
        {
            get { return (CrmDateTime)base.GetValue(AgeProperty); }
            set { base.SetValue(AgeProperty, value); }
        }

        private ICrmService _CrmService;
        public static readonly DependencyProperty AgeProperty = DependencyProperty.Register("Age", typeof(CrmDateTime), typeof(CloseSalesOrdersActivity));

        private int LoadInvoiceCount(Guid salesOrderId)
        {
            var query = String.Format(invoiceCountFetchXml, salesOrderId);
            var responseXml = _CrmService.Fetch(query);

            var resultSet = XElement.Parse(responseXml);
            var count = from r in resultSet.Elements("result")
                        select r.Element("count");

            return Convert.ToInt32(count.First().Value);
        }

        private SalesOrderSummary LoadSalesOrder(Guid salesOrderId)
        {
            var query = new QueryExpression { EntityName = EntityName.salesorder.ToString(), ColumnSet = new AllColumns() };
            query.Criteria.AddCondition("salesorderid", ConditionOperator.Equal, salesOrderId);

            var response = (RetrieveMultipleResponse)_CrmService.Execute(new RetrieveMultipleRequest { ReturnDynamicEntities = true, Query = query });
            var salesOrders = response.BusinessEntityCollection.BusinessEntities.Select(x => new SalesOrderSummary
                              {
                                  OrderId = ((Key)((DynamicEntity)x)["salesorderid"]).Value,
                                  Amount = ((CrmMoney)((DynamicEntity)x)["totalamount"]),
                                  InvoiceCount = 0
                              });

            if (salesOrders.Count() == 0)
                throw new ArgumentException(String.Format("There is no sales order with id: {0}", salesOrderId));

            var summary = salesOrders.ToArray()[0];
            summary.InvoiceCount = LoadInvoiceCount(salesOrderId);
            return summary;
        }

        private void FulFillSalesOrder(Lookup salesOrderLookup, DateTime closeDate)
        {
            var close = new orderclose { salesorderid = salesOrderLookup, actualend = new CrmDateTime(closeDate.ToString("u")) };
            var fulfill = new FulfillSalesOrderRequest { OrderClose = close, Status = -1 };

            try
            {
                _CrmService.Execute( fulfill );
            }
            catch ( SoapException ex )
            {
                Trace.TraceWarning( String.Format( "Cannot mark the order as fulfilled ({0})", ex.Message ) );
            }
        }

        private void CancelSalesOrder(Lookup salesOrderLookup, DateTime closeDate)
        {
            var close = new orderclose { salesorderid = salesOrderLookup, actualend = new CrmDateTime(closeDate.ToString("u")) };
            var cancel = new CancelSalesOrderRequest { OrderClose = close, Status = -1 };

            try
            {
                _CrmService.Execute( cancel );
            }
            catch ( SoapException ex )
            {
                Trace.TraceWarning( String.Format( "Cannot mark the order as canceled ({0})", ex.Message ) );
            }
        }

        private void CloseSalesOrder(Lookup salesOrderLookup, DateTime closeDate)
        {
            var salesOrder = LoadSalesOrder(salesOrderLookup.Value);
            if (salesOrder.InvoiceCount > 0)
                FulFillSalesOrder(salesOrderLookup, closeDate);
            else
                CancelSalesOrder(salesOrderLookup, closeDate);
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            var contextService = executionContext.GetService<IContextService>();
            var workflowContext = contextService.Context;
            _CrmService = workflowContext.CreateCrmService();

            var query = new QueryExpression { EntityName = EntityName.salesorder.ToString(), ColumnSet = new ColumnSet() };
            var createdOn = new ConditionExpression("createdon", ConditionOperator.OnOrBefore, Age);
            var statecode = new ConditionExpression("statecode", ConditionOperator.Equal, (int)SalesOrderState.Active);
            var filter = new FilterExpression { FilterOperator = LogicalOperator.And };
            filter.AddCondition(createdOn);
            filter.AddCondition(statecode);

            query.Criteria.AddFilter(filter);

            var response = (RetrieveMultipleResponse)_CrmService.Execute(new RetrieveMultipleRequest { ReturnDynamicEntities = true, Query = query });
            var opportunities = response.BusinessEntityCollection.BusinessEntities.Select(x => new Lookup("salesorder", ((Key)((DynamicEntity)x)["salesorderid"]).Value));
            foreach (var item in opportunities)
            {
                CloseSalesOrder(item, DateTime.Now);
            }

            return base.Execute(executionContext);
        }
    }
}
